#include "FileReaders.h"
#include<string.h>

/*
This file contains the source code for the file reader that parses the Wavefront OBJ file format. Since the file format supports a myriad of
three dimensional structures, accurately parsing each and every aspect of the OBJ file is a bit tedious. Hence we begin with the basic functionality
of parsing only the vertices, normals and texture coords. We will document each and every aspect of functionality addition to the file reader.
We use STL vectors internally as it results in cleaner code.

Date: 25-Oct-2010
Author: Srinath
Functionality: Vertex, Normal and texture coords parsing
*/

/*
Function	: Constructor
Input Params: @1 - const char* - pointer to a string that is the file path to the file to be opened
Return type	: None
Desc		: Explicit initialization constructor
*/
ObjFileReader::ObjFileReader(const char* pFilePath)
{
	// Check if file is good to go
	input_file.open(pFilePath);
	
	// TODO --->
	// Error checking to be added Later
	//if(input_file.bad())
	//	input_file.close();
	
}

/*
Function	: TokenizeString
Input Params: char* - pointer to a string
Return type : char** - pointer to tokens of strings
Desc		: This is an utility function that we use to tokenize strings
*/
char** ObjFileReader::TokenizeString(char* string)
{
	return NULL;
}


/*
Function	: ParseObjFile
Input Params: None
Return Type	: bool - indicating succes/failure of the parsing of the object file
Desc		: This function parses the given obj file
*/
bool ObjFileReader::ParseObjFile()
{
	// Date: 25-Oct-2010
	// Basic functionalities added
	// Reading vertices alone
	
	// allocate buffer
	int buffer_size = 1024;
	char* buffer = new char[buffer_size];
	
	// Parse till end of file
	while(!input_file.eof())
	{
		/*
		Basic reading mechanism:
		------------------------
		we read a line from the file. Check if the first character is 'v' followed by space/n/t to finalize that string as 
		# Vertex string
		# Vertex normal string
		# Texture coord string
		*/
		
		input_file.getline(buffer, buffer_size, '\n');
		
		// Parse Vertices
		if(strstr(buffer, "v "))
		{
			// vertex line
			char* token = strtok(buffer, " ");	// this would be 'V'. Subsequent calls to strtok would yeild the actual vertices
			int token_no = 1;
			float x,y,z;						// the actual vertex coords
			while(token != NULL)
			{
				// get the vertex coords in x,y,z
				token = strtok(NULL, " ");
				if(token_no == 1) { x = float(atof(token)); }
				else if(token_no == 2) { y = float(atof(token)); }
				else if(token_no == 3) { z = float(atof(token)); }

				// increment token number
				token_no++;
			}

			// We have all x,y,z. Use it to create a vertex and fill it inside the vertex buffer
			Vector3 vertex(x, y, z);
			vertex_buffer.push_back(vertex);
			// reset token_no
			//token_no = 1;
		}

		// Parse Normals
		if(strstr(buffer, "vn"))
		{
			// vertex line
			char* token = strtok(buffer, " ");	// This would be vn. Subsequent calls to strtok would yield the coords of normal
			int token_no = 1;
			float x,y,z;
			while(token != NULL)
			{
				// get the normal coords as x,y,z
				token = strtok(NULL, " ");
				if(token_no == 1) { x = float(atof(token)); }
				else if(token_no == 2) { y = float(atof(token)); }
				else if(token_no == 3) { z = float(atof(token)); }

				// increment the token number
				token_no++;
			}

			// Now that we have the x,y,z coords of the normal we can add it to the normal buffer
			Vector3 normal(x, y, z);
			normal_buffer.push_back(normal);
			// reset token number
			//token_no = 1;
		}

		// Parse texture coords
		if(strstr(buffer, "vt"))
		{
			// texture coord line
			char* token = strtok(buffer, " ");	// This would be vt. now we will have to extract the texture coords
			int token_no = 1;
			float x,y,z;						// Assuming three dimensional texture coords are given for the vertex
			while(token != NULL)
			{
				// get the texture coords
				token = strtok(NULL, " ");
				if(token_no == 1) { x = float(atof(token)); }
				else if(token_no == 2) 
				{
					y = float(atof(token));
					// assuming 2 dimensional texture coords are in major use
					// we set z to 0.0f. But incase if the third component of coords exist, we will add it in next iteration
					z = 0.0f;
				}
				else if(token_no == 3) { z = float(atof(token)); }
			}

			// Now we have the three components of the coords. Create a vertex and push it down the texture buffer
			Vector3 tex(x, y, z);
			texture_coords.push_back(tex);
		}

		// Parse faces
		if(strstr(buffer, "f "))
		{
			// First we find the token count of the polygonal face to allocate appropriate amount of space for the details of vertices
			char *temp = new char[buffer_size];
			strcpy(temp, buffer);
			char* pcnt_token = strtok(temp, " ");
			int polygon_vertex_count = 0;
			while(pcnt_token != NULL)
			{
				// get next token
				pcnt_token = strtok(NULL, " ");
				if(pcnt_token) polygon_vertex_count++;
			}
			// delete temp space
			delete[] temp;

			// Create appropriate memory for all details
			int* vertex_pos_indices = new int[polygon_vertex_count];
			int* vertex_nor_indices = new int[polygon_vertex_count];
			int* vertex_tex_indices = new int[polygon_vertex_count];
			int pcount = 0;
			bool bv, bn, bt;		// all possible combination of vertex formats
			bv = bn = bt = false;	// set all to false initially

			char* token = strtok(buffer, " ");
			while(token != NULL)
			{
				token = strtok(NULL, " ");
				if(strstr(token, "//"))
				{
					// This would mean that we dont have texture coords
					// just pos coords and normal coords
					// hence set flags appropriately
					bv = bn = true;					
					char* temp = new char[1024];
					strcpy(temp, token);
					char* token2 = strtok(temp, "//");
					vertex_pos_indices[pcount] = atoi(token2);
					while(token2 != NULL)
					{
						token2 = strtok(NULL, "//");
						vertex_nor_indices[pcount] = atoi(token2);
					}
					// increment pcount
					pcount++;
					delete[] temp;
				}
				else
				{
					// else the vertex format could have just pos/tex or pos/tex/nor form
					// we can move that using a temp number to indicate which is which
					// if temp = 1 then tex
					// if temp = 2 then normal
					char* temp = new char[1024];
					strcpy(temp, token);
					char* token2 = strtok(temp, "/");
					int itemp = 1;
					vertex_pos_indices[pcount] = atoi(token2);
					while(token2 != NULL)
					{
						token2 = strtok(NULL, "/");
						if(itemp == 1) 
						{
							vertex_tex_indices[pcount] = atoi(token2); 
							itemp++;
							bt = true;			// assuming only pos/tex format
						}
						else if(itemp == 2) 
						{
							vertex_nor_indices[pcount] = atoi(token2);
							bn = true;			// ok. we have a pos/tex/nor format
						}
					}
					pcount++;
					// delete memory allocated
					delete[] temp;
				}
			}
			/* one polygon face is done
			time to triangulate !!!!!!
			
			Note: Triangulation scheme:
			----------------------------
			Consider face has n polygons as [0,1,2, -- n-1]
			Then we triangulate that polygon as follows.
			
			loop index = 1 till n-2
			triangle[index] = vertex[0], vertex[index], vertex[index+1]
			index++;
			*/
			// the general rule is that if we have n vertices, the n-2 triangles
			int count = 0;
			Vector3 vertex0, vertex1, vertex2, normal0, normal1, normal2, tex0, tex1, tex2;
			
			// get the first vertex details
			vertex0 = vertex_buffer.at(vertex_pos_indices[0]);
			if(bt)
				tex0 = texture_coords.at(vertex_tex_indices[0]);
			if(bn)
				normal0 = normal_buffer.at(vertex_nor_indices[0]);
			
			for(int index = 1; index <= polygon_vertex_count-2; index++)
			{
				vertex1 = vertex_buffer.at(vertex_pos_indices[index]);
				vertex2 = vertex_buffer.at(vertex_pos_indices[index+1]);
				if(bn)
				{
					normal1 = normal_buffer.at(vertex_nor_indices[index]);
					normal2 = normal_buffer.at(vertex_nor_indices[index+1]);
				}
				if(bt)
				{
					tex1 = texture_coords.at(vertex_tex_indices[index]);
					tex2 = texture_coords.at(vertex_tex_indices[index+1]);
				}

				TriangleVNT triangle(vertex0, vertex1, vertex2, normal0, normal1, normal2, tex0, tex1, tex2);
				triangle_list.push_back(triangle);
			}
		}
	}
	
	// close file
	input_file.close();
	return true;
}